package com.Leo.Game;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by huxin on 2017/2/22.
 */
public class sudoku {
    static int[][] sudokuArr = {
            //0  1  2  3  4  5  6  7  8  9
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 0
            {0, 9, 5, 0, 0, 0, 4, 0, 0, 0}, // 1
            {0, 2, 4, 6, 0, 7, 0, 0, 5, 9}, // 2
            {0, 7, 8, 0, 6, 0, 9, 2, 3, 4}, // 3
            {0, 0, 6, 0, 7, 1, 3, 0, 0, 2}, // 4
            {0, 3, 0, 0, 0, 0, 0, 0, 0, 7}, // 5
            {0, 1, 0, 0, 9, 4, 2, 0, 8, 3}, // 6
            {0, 5, 9, 8, 2, 0, 6, 0, 7, 1}, // 7
            {0, 4, 1, 0, 0, 8, 0, 3, 9, 6}, // 8
            {0, 6, 0, 0, 4, 0, 0, 0, 2, 5}  // 9
    };

    public static void printSudoku(){
        StringBuilder stringBuilder = new StringBuilder(256);
        for (int i = 1; i < sudokuArr.length; ++i){
            for (int j = 1; j < sudokuArr[i].length; ++j){
                stringBuilder.append(sudokuArr[i][j]);
                stringBuilder.append(" ");
            }
            stringBuilder.append('\n');
        }

        System.out.println(stringBuilder);
    }

    static final int[] numLimit = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    /**
     * 判断数值是不是未知的
     */
    public static boolean IsUnKnown(int value) {
        return value == 0;
    }

    /**
     * 获得行层级，每3行为一级
     * 用来帮助获得某一区域相关x,y坐标
     */
    public static int getRowLevel(int Field) {
        int Level = 1;
        while (true) {
            Field -= 3;
            if (Field > 0) {
                Level += 1;
            } else {
                break;
            }
        }
        return Level;
    }

    /**
     * 获得列的层级，每3列为一级
     * 用来帮助获得某一区域相关x,y坐标
     */
    public static int getColLevel(int Field) {
        while (true) {
            if (Field > 3) {
                Field -= 3;
            } else {
                return Field;
            }
        }
    }

    public static int getValueOfPos(int row, int col) {
        return sudokuArr[row][col];
    }

    public static void setValueOfPos(int row, int col, int value) {
        sudokuArr[row][col] = value;
    }

    /**
     * 获得区域编号中的首坐标
     */
    public static Pair<Integer, Integer> getFieldToTagPos(int Field) {

        if (Field <= 0 && Field > 9) {
            System.out.println("区域出错：" + Field + " limit: 1 ~ 9");
            return null;
        }
        //获得层级
        int rowlvl = getRowLevel(Field);
        int collvl = getColLevel(Field);

        //通过使用行与列的层级可以得出相应的相对比较坐标，也就是每一个区域的首坐标
        //通过使用坐标来确定某一区域内的相应row, col坐标中的的值

        int rowTag = 1;
        int colTag = 1;

        //最低层级不需要进行改变
        if (rowlvl != 1)
            rowTag += (rowlvl - 1) * 3; // 相应首坐标的row

        if (collvl != 1)
            colTag += (collvl - 1) * 3; // 相应首坐标的col

        return new Pair<>(rowTag, colTag);
    }

    /**
     * 通过获得的区域首坐标得到对应二维数组坐标
     */
    public static Pair<Integer, Integer> getPos(Pair pair, int row, int col) {

        if (row > 3 && row < 1) {
            System.out.println("行引索出错：" + row + " limit: 1 ~ 3");
            return null;
        }

        if (col > 3 && col < 1) {
            System.out.println("列引索出错：" + col + " limit: 1 ~ 3");
            return null;
        }

        row = (int) pair.first + row - 1; //最终row坐标
        col = (int) pair.second + col - 1; //最终col坐标
        return new Pair<>(row, col);
    }

    /**
     * 获得某个区域中相对位置上的值
     */
    public static int getValueOfFieldPos(int Field, int row, int col) {

        Pair pair = getFieldToTagPos(Field);
        if (pair == null)
            return 0;

        Pair pos = getPos(pair, row, col);
        if (pair == null)
            return 0;

        return getValueOfPos((int) pos.first, (int) pos.second);
    }

    /**
     * 通过遍历区域中的元素得到数据集
     */
    public static List<Integer> getFieldCurrKnownElement(int Field) {
        List<Integer> list = new ArrayList();

        for (int i = 1; i <= 3; ++i) {
            for (int j = 1; j <= 3; ++j) {
                int value = getValueOfFieldPos(Field, i, j);
                if (value != 0)
                    list.add(value);
            }
        }

        return list;
    }

    /**
     * 通过得到的已知数集数，得到相应未填入空中的数据集
     * 数据集中的元素就是未填空的可能集
     */
    public static List<Integer> getFieldCurrUnKnownElement(int Field) {
        List<Integer> list = getFieldCurrKnownElement(Field);

        int[] numLimit = sudoku.numLimit.clone();

        for (int value :
                list) {
            numLimit[value] = 0;
        }

        list = new ArrayList<>();
        for (int value :
                numLimit) {
            if (value != 0)
                list.add(value);
        }

        return list;
    }

    /**
     * 获得区域内未知的数据的坐标
     */
    public static List<Pair<Integer, Integer>> getFieldUnKnownPos(int Field) {
        Pair<Integer, Integer> fieldFirstPos = getFieldToTagPos(Field);
        List<Pair<Integer, Integer>> pairList = new ArrayList<>();
        for (int i = 1; i <= 3; ++i) {
            for (int j = 1; j <= 3; ++j) {
                int row = fieldFirstPos.first + i - 1;
                int col = fieldFirstPos.second + j - 1;

                int value = getValueOfPos(row, col);
                if (IsUnKnown(value)) //value == 0
                    pairList.add(new Pair<>(row, col));
            }
        }

        return pairList;
    }

    /**
     * 通过使用对照表来判断每个区域是否存在重复值
     */
    public static boolean IsSudokuArrFieldRight() {

        for (int i = 1; i <= 9; ++i) {
            int[] numLimit = sudoku.numLimit.clone();
            List<Integer> list = getFieldCurrKnownElement(i);

            for (int value :
                    list) {
                if (numLimit[value] == 0) {
                    System.out.println("区域内存在两个相同的数据元素!");
                    return false;
                }
                numLimit[value] = 0;
            }
        }

        return true;
    }

    /**
     * 获得某一行有那些数存在
     */
    public static int[] getPosRowDateSet(int row) {
        int[] rowArr = new int[10];
        for (int i = 1; i <= 9; ++i) {
            int index = getValueOfPos(row, i);
            rowArr[index] = index;
        }

        return rowArr;
    }

    /**
     * 获得某一列有那些数存在
     */
    public static int[] getPosColDateSet(int col) {
        int[] colArr = new int[10];
        for (int i = 1; i <= 9; ++i) {
            int index = getValueOfPos(i, col);
            colArr[index] = index;
        }

        return colArr;
    }

    public static void FieldProcess(int Field) {
        //获得可能集
        List elemList_ = getFieldCurrUnKnownElement(Field);
        if(elemList_.size() == 0)
            return;

        Integer[] unKnownList = new Integer[elemList_.size()];
        elemList_.toArray(unKnownList);

        //获得未知空数坐标集
        List posList_ = getFieldUnKnownPos(Field);
        if(posList_.size() == 0)
            return;

        Pair<Integer, Integer>[] posList = new Pair[posList_.size()];
        posList_.toArray(posList);

        //根据某一个未知空的坐标进行横竖扫描排除干扰得到唯一确定值
        for (Pair<Integer, Integer> pos :
                posList) {
            Integer[] tempArr = unKnownList.clone();

            //横竖扫描, 按照key=>bool
            int[] rowSet = getPosRowDateSet(pos.first);
            int[] colSet = getPosColDateSet(pos.second);

            for (int i = 0; i < tempArr.length; i++) {
                //对可能集进行筛除

                //获得可能数
                int value = tempArr[i];

                //判断可能数是否存在横竖集合中
                if (rowSet[value] != 0 && tempArr[i] != 0)
                    tempArr[i] = 0; //存在，则将这个可能数删除
                if (colSet[value] != 0 && tempArr[i] != 0)
                    tempArr[i] = 0;
            }

            //可能集排查结束
            int valueNum = 0, resultIndex = 0;

            for (int i = 0; i < tempArr.length; i++) {
                if (tempArr[i] != 0) {
                    //获得可能集中未被删除的数量,与索引
                    ++valueNum;
                    resultIndex = i;
                }
            }

            if (valueNum <= 0)
                System.out.println("区域排查出错");
            else if (valueNum == 1) {
                //更新可能集,从可能集中删除
                unKnownList[resultIndex] = 0;
                //更新总表
                setValueOfPos(pos.first, pos.second, tempArr[resultIndex]);
            }else {
                //出现可能性不唯一的情况，不做任何操作
                System.out.println("未找到唯一值 x, y: " + pos.first + ", " + pos.second);
            }
        }
    }

    public static void sudokuPorcess(){
        for(int i = 1; i <= 9; ++i){
            FieldProcess(i);
            for (int j = i; j >= 1; --j){
                FieldProcess(j);
            }
        }
    }
}
